using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Boa.HD.AccesoDatos.Entidades;
using Boa.HD.AccesoDatos.Contratos;
using Boa.HD.Negocios.Servicios;
using Boa.HD.Negocios.Excepciones;
using Boa.HD.Negocios.Session;
using Boa.HD.AccesoDatos.Entidades.DTO;
using Boa.HD.AccesoDatos.Entidades.Utils;

namespace Boa.HD.Negocios
{
   /// <summary>
   /// Implementacion de la interface IHD_HistoriaProblemaAsistidoNegociosServicio, los metodos que estan definidos en el contrato IHD_HistoriaProblemaAsistidoNegociosServicio serán accesibles desde un contexto externo
   /// </summary>	
   public class HD_HistoriaProblemaAsistidoNegocios : IHD_HistoriaProblemaAsistidoNegociosServicio
   {
	private IHD_HistoriaProblemaAsistidoAccesoDatos accesoDatos;
	public HD_HistoriaProblemaAsistidoNegocios()
	{		
		accesoDatos = DataFactory.Current.GetHD_HistoriaProblemaAsistidoAccesoDatos();
	}
	
	/// <summary>
    /// Devuelve una lista de HD_HistoriaProblemaAsistido's de las ocurrencias encontradas a partir del filtro
    /// </summary>    
	public List<HD_HistoriaProblemaAsistido> BuscarPorFiltro(string filtro)
	{
		return accesoDatos.BuscarPorFiltro(filtro).ToList();
	}

	#region operaciones básicas
	
	/// <summary>
    /// Elimina la entidad HD_HistoriaProblemaAsistido, lanzará una excepcion si la entidad guarda dependencia a otras entidades
    /// </summary>	
	public void Eliminar(HD_HistoriaProblemaAsistido entidad)
	{
		try
		{
		    accesoDatos.Delete(accesoDatos.GetById(entidad.Id));
	    }
		catch(Exception ex)
		{
		   throw new HD_HistoriaProblemaAsistidoEliminarExcepcion("HD_HistoriaProblemaAsistido");
		}
	}
	
	/// <summary>
    /// Devuelve HD_HistoriaProblemaAsistido, buscada por su Id, si no existe, entonces devuelve null
    /// </summary>	
	public HD_HistoriaProblemaAsistido ObtenerPorId(object Id)
	{
		return accesoDatos.GetById(Id);
	}

	/// <summary>
    /// Devuelve List<HD_HistoriaProblemaAsistido>, de todas la entidades de ese tipo
    /// </summary>	
	public List<HD_HistoriaProblemaAsistido> ObtenerTodas()
	{
		return accesoDatos.GetAll().ToList();
	}

	
	/// <summary>
    /// Guarda HD_HistoriaProblemaAsistido, previa ejecucion satisfactoria de las reglas de negocio
    /// </summary>	
	public HD_HistoriaProblemaAsistido Guardar(HD_HistoriaProblemaAsistido entidad)
	{
		//Agregar aqui las reglas de negocio.......	
		if(entidad==null)
		 throw new HD_HistoriaProblemaAsistidoRequeridoExcepcion(); 
		
		#region Foreign Keys Values
        if(entidad.AsignacionTecnicoProblemaId_FK > 0 && entidad.AsignacionTecnicoProblema==null)
            entidad.AsignacionTecnicoProblema = DataFactory.Current.GetHD_AsignacionTecnicoProblemaAccesoDatos().GetById(entidad.AsignacionTecnicoProblemaId_FK);
        if(entidad.EstadoTrabajoId_FK > 0 && entidad.EstadoTrabajo==null)
            entidad.EstadoTrabajo = DataFactory.Current.GetHD_EstadoTrabajoAccesoDatos().GetById(entidad.EstadoTrabajoId_FK);
        if(entidad.HistorialProblemaPadreId_FK > 0 && entidad.HistorialProblemaPadre==null)
            entidad.HistorialProblemaPadre = DataFactory.Current.GetHD_HistoriaProblemaAsistidoAccesoDatos().GetById(entidad.HistorialProblemaPadreId_FK);
        if(entidad.SolicitudAsistenciaId_FK > 0 && entidad.SolicitudAsistencia==null)
            entidad.SolicitudAsistencia = DataFactory.Current.GetHD_SolicitudAsistenciaAccesoDatos().GetById(entidad.SolicitudAsistenciaId_FK);
        #endregion


        if (entidad.Id == 0)
        {
            entidad.Guid = System.Guid.NewGuid().ToString();
            accesoDatos.Insert(entidad);
        }
        else
            accesoDatos.Update(entidad);

		return entidad;
	}


    public void ActualizarEstado(DtoResumenHistoraProblema entidad)
    {
        HD_EstadoTrabajo estadoEnespera = new HD_EstadoTrabajoNegocios().GetEstadoPorEnumerador(EstadoTrabajoEnum.EnEspera);

        if (entidad.EstadoTrabajoId != estadoEnespera.Id)
            throw new Exception("Solo es permitido cambio de estado si inicialmente esta en Espera");


        HD_HistoriaProblemaAsistido paraActualizar = ObtenerPorId(entidad.HistorialProblemaAsistidoUltimo);

        var tmpPadre = paraActualizar.Id;

        accesoDatos.Evitar(paraActualizar);

        paraActualizar.Id = 0;
        paraActualizar.HistorialProblemaPadre = null;
        paraActualizar.HistorialProblemaPadreId_FK = tmpPadre ;
        paraActualizar.DescripcionHistoria = "Cambio de Prioridad de "+ PrioridadProblema.ListaPrioridad().Where(p=>p.Llave.Equals(paraActualizar.PrioridadCalculada.ToString())).FirstOrDefault().Nombre+" a "+
                                                                        PrioridadProblema.ListaPrioridad().Where(p => p.Llave.Equals(entidad.PrioridadCalculada.ToString())).FirstOrDefault().Nombre;
        paraActualizar.FechaRegistro = DateTime.Now;
        paraActualizar.PrioridadCalculada = entidad.PrioridadCalculada;
        paraActualizar.MotivoCambioEstado = entidad.MotivoCambioEstado;
        paraActualizar.Guid = System.Guid.NewGuid().ToString();
        Guardar(paraActualizar);

        //aqui guardar en los logs de los cambios
    }
    
	#endregion


    public List<HD_HistoriaProblemaAsistido> HistorialProblemaPorTecnico()
    {
        var usuario = Global.UserInfo();
        return accesoDatos.ListaUltimosNodosDeProblema(usuario.DtoBOAPersonal.EmpleadoId).ToList();        
    }    

    public List<DtoResumenHistoraProblema> ListaParaAdministrador(int Prioridad, string filtro, int EstadoTrabajoId)
    {
        return accesoDatos.GetResumenHistoriaProblema(filtro, EstadoTrabajoId, Prioridad).ToList();
    }

    public HD_HistoriaProblemaAsistido AtenderSolicitud(int HistoriaId, bool NotificadoComoBug = false, string Descripcion="")
    {
        HD_HistoriaProblemaAsistido historia = accesoDatos.GetById(HistoriaId);

        HD_HistoriaProblemaAsistido nuevo = new HD_HistoriaProblemaAsistido();
        
        nuevo.AsignacionTecnicoProblema = historia.AsignacionTecnicoProblema;
        nuevo.Cerrado = false;

        if (!NotificadoComoBug)
        {
            nuevo.EstadoTrabajo = DataFactory.Current.GetHD_EstadoTrabajoAccesoDatos().GetAll().Where(p => p.Enumerador == (int)GetEstadoSiguiente(historia.EstadoTrabajo.Enumerador)).FirstOrDefault();
            nuevo.DescripcionHistoria = "Cambio de " + historia.EstadoTrabajo.Nombre + " a " + nuevo.EstadoTrabajo.Nombre;            
            nuevo.DescripcionProblema = historia.SolicitudAsistencia.DescripcionProblema;
            nuevo.DescripcionSolucion = Descripcion;
        }
        else
        {
            nuevo.EstadoTrabajo = DataFactory.Current.GetHD_EstadoTrabajoAccesoDatos().GetAll().Where(p => p.Enumerador == (int)EstadoTrabajoEnum.Atendiendo).FirstOrDefault();
            nuevo.DescripcionHistoria = "Bugtrack de Usuario : " + Descripcion;
            nuevo.DescripcionProblema = Descripcion;
        }

        
        nuevo.FechaInicioAsistido = DateTime.Now;
        nuevo.FechaRegistro = DateTime.Now;
        nuevo.HistorialProblemaPadre = historia;
        nuevo.PrioridadCalculada = historia.PrioridadCalculada;
        nuevo.SolicitudAsistencia = historia.SolicitudAsistencia;
        nuevo.Solucionado = false;

        return Guardar(nuevo);
    }

    public EstadoTrabajoEnum GetEstadoSiguiente(int EstadoEnum)
    {
        switch (EstadoEnum)
        {
            case 1: return EstadoTrabajoEnum.EsperandoConfirmacion;
            case 2: return EstadoTrabajoEnum.Atendiendo;
            case 3: return EstadoTrabajoEnum.Solucionado;
            case 4: return EstadoTrabajoEnum.Cerrado;
            default: return EstadoTrabajoEnum.Ninguno;        
        }
    }


    public void AsignarSoporteTecnico(string GuidSolicitudAsistencia)
    {        
        //obtener prioridad del problema solicitado por GuidSolicitudAsistencia
        HD_SolicitudAsistencia solicitudAsistencia = DataFactory.Current.GetHD_SolicitudAsistenciaAccesoDatos().GetAll().Where(p => p.GuidSolicitudAsistencia.Equals(GuidSolicitudAsistencia)).FirstOrDefault();
        HD_DefinicionProblema definicionProblema = solicitudAsistencia.DefinicionProblema;

        var InfoUsuario = Session.Global.UserInfo().DtoBOAPersonal;
        HD_NivelJerarquico nivelJerarquico =  DataFactory.Current.GetHD_NivelJerarquicoItemAccesoDatos().GetAll().Where(p => p.BOA_NivelJerarquicoId == InfoUsuario.NivelJerarquicoId).FirstOrDefault().NivelJerarquico;


        HD_Severidad severidad = DataFactory.Current.GetHD_SeveridadAccesoDatos().GetAll().
                                    Where(p => p.DefinicionProblema.Id == definicionProblema.Id  && 
                                               p.BOA_CargoId == InfoUsuario.CargoId).FirstOrDefault();        
        //si severidad es nulo, agarrar prioridad de la jerarquia.
        HD_PrioridadTecnica prioridadSeveridad = severidad == null ? nivelJerarquico.PrioridadTecnica : severidad.PrioridadTecnica;


        HD_SeveridadPrioridadConfigItem itemFinal =   DataFactory.Current.GetHD_SeveridadPrioridadConfigItemAccesoDatos().GetAll().
                                                        Where(p => p.SeveridadPrioridadConfig.PrioridadProblema.Id == definicionProblema.PrioridadTecnica.Id &&
                                                              p.SeveridadPrioridadConfig.PrioridadSeveridad.Id == prioridadSeveridad.Id &&
                                                              p.NivelJerarquicoId == nivelJerarquico.Id).FirstOrDefault();

        int PrioridadCalculada = itemFinal != null ? itemFinal.SeveridadPrioridadConfig.PrioridadFinal.Prioridad : 4;
        //lista la disponibilidad de los tecnicos en orden ascendente y capturamos el primero de la lista (se supone q es el que menos tarea asignada tiene)
        List<DtoTecnicoHoraAsignada> asignacionesTecnico = DataFactory.Current.GetHD_AsignacionTecnicoProblemaAccesoDatos().TecnicosConTareasAsignadas(solicitudAsistencia.DefinicionProblema.Id).ToList();
        if (asignacionesTecnico == null || !asignacionesTecnico.Any())
        {

            /*************************************************************************************
             *    AGREGAR AL TECNICO EXTERNO (admin) COMO TECNICO ASISTENTE
             ************************************************************************************/
            IHD_AsignacionTecnicoProblemaNegociosServicio externoNegocios = new HD_AsignacionTecnicoProblemaNegocios();
            asignacionesTecnico = externoNegocios.GuardarAsignacionParaTecnicoExterno(definicionProblema);


            //throw new NoExisteTecnicoAsignadoAProblemaExcepcion(solicitudAsistencia.DefinicionProblema.Nombre);
        }

        HD_AsignacionTecnicoProblema tecnicoAsignado = DataFactory.Current.GetHD_AsignacionTecnicoProblemaAccesoDatos().GetById(asignacionesTecnico.First().AsignacionTecnicoProblemaId);

        HD_HistoriaProblemaAsistido problemaParaAsistir = new HD_HistoriaProblemaAsistido();
        problemaParaAsistir.FechaRegistro = DateTime.Now;
        problemaParaAsistir.FechaInicioAsistido = DateTime.Now;
        problemaParaAsistir.DescripcionHistoria = "Problema Asignado por el sistema a " + tecnicoAsignado.Tecnico.Nombre;
        problemaParaAsistir.DescripcionProblema = solicitudAsistencia.DescripcionProblema;
        problemaParaAsistir.EstadoTrabajo = DataFactory.Current.GetHD_EstadoTrabajoAccesoDatos().GetAll().Where(p => p.Enumerador == (int)EstadoTrabajoEnum.EnEspera).FirstOrDefault();
        problemaParaAsistir.SolicitudAsistencia = solicitudAsistencia;
        problemaParaAsistir.Solucionado = false;
        problemaParaAsistir.Cerrado = false;
        problemaParaAsistir.AsignacionTecnicoProblema = tecnicoAsignado;
        problemaParaAsistir.PrioridadCalculada = PrioridadCalculada;

        Guardar(problemaParaAsistir);



    }

    #region Obsoleto Algoritmo de Asignacion de Tecnico por prioridad

    [Obsolete]
    public void AsignarSoporteTecnico_Obsoleto(string GuidSolicitudAsistencia)
    {
        //Recupera nivel jerarquico del usuario que ha iniciado sesion (solicitante de soporte tecnico)
        //NivelJerarquico: 1 = Muy Alta, 2 = Alta, 3 = Media, 4 = Baja
        var InfoUsuario = Session.Global.UserInfo().DtoBOAPersonal;
        int NivelJerarquico = Session.Global.UserInfo().DtoBOAPersonal.NivelJerarquico;

        //obtener prioridad del problema solicitado por GuidSolicitudAsistencia
        HD_SolicitudAsistencia solicitudAsistencia = DataFactory.Current.GetHD_SolicitudAsistenciaAccesoDatos().GetAll().Where(p => p.GuidSolicitudAsistencia.Equals(GuidSolicitudAsistencia)).FirstOrDefault();
        int PrioridadProblema = solicitudAsistencia.DefinicionProblema.PrioridadTecnica.Prioridad;

        //calculamos la prioridad en base al promedio entre NivelJerarquico y PrioridadProblema
        int PrioridadCalculada = CalcularPrioridad(NivelJerarquico, PrioridadProblema);

        //lista la disponibilidad de los tecnicos en orden ascendente y capturamos el primero de la lista (se supone q es el que menos tarea asignada tiene)
        List<DtoTecnicoHoraAsignada> asignacionesTecnico = DataFactory.Current.GetHD_AsignacionTecnicoProblemaAccesoDatos().TecnicosConTareasAsignadas(solicitudAsistencia.DefinicionProblema.Id).ToList();
        if (asignacionesTecnico == null || asignacionesTecnico.Count() == 0)
        {
            throw new NoExisteTecnicoAsignadoAProblemaExcepcion(solicitudAsistencia.DefinicionProblema.Nombre);
        }

        HD_AsignacionTecnicoProblema tecnicoAsignado = DataFactory.Current.GetHD_AsignacionTecnicoProblemaAccesoDatos().GetById(asignacionesTecnico.First().AsignacionTecnicoProblemaId);

        HD_HistoriaProblemaAsistido problemaParaAsistir = new HD_HistoriaProblemaAsistido();
        problemaParaAsistir.FechaRegistro = DateTime.Now;
        problemaParaAsistir.FechaInicioAsistido = DateTime.Now;
        problemaParaAsistir.DescripcionHistoria = "Problema Asignado por el sistema a " + tecnicoAsignado.Tecnico.Nombre;
        problemaParaAsistir.DescripcionProblema = solicitudAsistencia.DescripcionProblema;
        problemaParaAsistir.EstadoTrabajo = DataFactory.Current.GetHD_EstadoTrabajoAccesoDatos().GetAll().Where(p => p.Enumerador == (int)EstadoTrabajoEnum.EnEspera).FirstOrDefault();
        problemaParaAsistir.SolicitudAsistencia = solicitudAsistencia;
        problemaParaAsistir.Solucionado = false;
        problemaParaAsistir.Cerrado = false;
        problemaParaAsistir.AsignacionTecnicoProblema = tecnicoAsignado;
        problemaParaAsistir.PrioridadCalculada = PrioridadCalculada;

        Guardar(problemaParaAsistir);
    }

    public int CalcularPrioridad(int NivelJerarquico, int PrioridadProblema)
    {
        decimal promedio = (Convert.ToDecimal(NivelJerarquico) + Convert.ToDecimal(PrioridadProblema)) / (Convert.ToDecimal(2));
        if (promedio.EstaEnRango(1, 1.5M))
            return 1; //prioridad Muy Alta
        if (promedio.EstaEnRango(1.6M, 2.5M))
            return 2; //Prioridad Alta
        if (promedio.EstaEnRango(2.6M, 3.5M))
            return 3; //Prioridad Media
        if (promedio.EstaEnRango(3.5M, 4))
            return 4; //Prioridad Baja       

        return 0;
    }

    #endregion




   }


   public static class PromedioExtension
   {
       public static bool EstaEnRango(this decimal promedio, decimal valor1, decimal valor2)
       {
           return promedio >= valor1 && promedio <= valor2;
       }
   }
}


